#!/usr/bin/env python

# A program to allow geolocation.  Allows the detection of location information,
# the geocoding of that information and the transformation of the resulting
# geocodes.  Uses a plugin system for flexibility.
# Copyright (C) 2010 Francis Markham
# This program is free software: you can redistribute it and/or modify it 
# under the terms of the GNU General Public License as published by 
# the Free Software Foundation, either version 3 of the License, or 
# (at your option) any later version. This program is distributed in the 
# hope that it will be useful, but WITHOUT ANY WARRANTY; without 
# even the implied warranty of MERCHANTABILITY or FITNESS FOR 
# A PARTICULAR PURPOSE. See the GNU General Public License 
# for more details. You should have received a copy of the GNU General 
# Public License along with this program. If not, see <http://www.gnu.org/licenses/>. 


import plugins, sys, os, optparse, misc
from misc import *

init_plugin_system()

#  Parse command-line
usage = "usage: %prog [options] [sniff]"
parser = optparse.OptionParser(version=("%prog " + misc.VERSION), usage=usage)

parser.add_option("-t", "--list",
                  action="store_true", dest="list_locators", 
                  help="List known locators, and exit.",
                  default=False)
parser.add_option("-s", "--listoutspecifiers", dest="list_specifiers",
                  action="store_true",
                  help="List valid output specifiers, and exit.",
                  default=False)
parser.add_option("-l", "--locator", dest="locator",
                  help="Locator to use to geolocate.",
                  default=None)
parser.add_option("-o", "--outputtype", dest="outputtype",
                  help="Output type to use.  " +
                  "Currently \"json\" and \"csv\" are supported but " +
                  "this can be extended with plugins.",
                  default="json")
parser.add_option("-f", "--outputformat", dest="outputformat",
                  help="Format specifier for csv output.  " +
                  "Takes the form \"abcd\" where a, b, c and d are output " +
                  "format specifiers.  Run with --listspecifiers for a list.",
                  default=None)
parser.add_option("-i", "--inputtype", dest="inputtype",
                  help="Input type to use. " +
                  "Currently \"json\" and \"csv\" are supported but " +
                  "this can be extended with plugins.",
                  default="json")
parser.add_option("-r", "--inputformat", dest="inputformat",
                  help="Format specifier for csv output.  Required if \"csv\" " +
                  "--inputtype is used. " +
                  "Takes the form \"abcd\" where a, b, c and d are output " +
                  "format specifiers.  Run with --listspecifiers for a list.",
                  default=None)
parser.add_option("-v", "--verbose",
                  action="store_true", dest="verbose", 
                  help="Verbose output.",
                  default=False)


(options, args) = parser.parse_args()

if len(args) == 1:
    input = [args[0]]
elif len(args) == 0:
    input = sys.stdin
else:
    print "ERROR -- Too many arguments."
    parser.print_help()
    sys.exit(1)

if options.list_locators:
    if options.verbose:
        print "Locator list:"
    for p in plugins.find_plugins():
        if plugins.Locator in p.__bases__:
            print p.name()
    sys.exit(0)

if options.list_specifiers:
    if options.verbose:
        print "Output specifiers:"
    for name, details in plugins.StandardNames.names.items():
        print "%s\t%s" % details
    sys.exit(0)

outputformatter = plugins.find_plugin_by_name(options.outputtype, plugins.Formatter)()
if not outputformatter:
    print_err("could not find plugin for output formatter \"%s\"." % (options.outputtype))
if outputformatter.needs_output_specifiers() and not options.outputformat:
    print_error("To use output type \"%s\" you must specify an output format with --outputformat." % outputformatter.name())

inputformatter = plugins.find_plugin_by_name(options.inputtype, plugins.Formatter)()
if not inputformatter:
    print_err("could not find plugin for input formatter \"%s\"." % (options.inputtype))
if inputformatter.needs_input_specifiers() and not options.inputformat:
    print_error("To use input type \"%s\" you must specify an input format with --inputformat." % inputformatter.name())

# We may have multiple sniffs to process, so do it in a loop
for line in input:
    try:
        info = inputformatter.parse_input(line, options.inputformat)
    except:
        print_err("Could not parse input \"%s\" with input formatter \"%s\"" % (line, options.inputtype))
        continue

    # If its not in a list, listify
    if type(info) != type([]):
        info = [info]

    # make sure our input types are correctly named
    input_types = []
    for key in info[0].keys():
        input_types.append(plugins.StandardNames.canonical_names[key])

    locator = None
    if options.locator:
        p = plugins.find_plugin_by_name(options.locator)
        if not p:
            print_err("Could not find a plugin \"%s\"." % options.locator)
        if not plugins.Locator in p.__bases__:
            print_err("Plugin \"%s\" is not a locator." % options.locator)
        locator = p()
        if not locator.can_run():
            print_error("Plugin \"%s\" cannot run." % options.locator)
    else:
        unrunnable = []
        for p in plugins.find_plugins():
            if (plugins.Locator in p.__bases__):
                if options.verbose:
                    print "Using locator %s" % p.name()
                if p.can_locate(input_types):
                    locator = p()
                    if locator.can_run():
                        break
                    else:
                        unrunnable.append(p.name())
                        locator = None
        if not locator:
            if len(unrunnable) > 0:
                print_err("All locators of are unrunnable.  They are \"%s\"." % (', '.join(unrunnable)))
            else:
                print_err("Could not find suitable locator")


    location = outputformatter.format_output(locator.locate(info), options.outputformat)
    print location

